package com.lbiuse.wx.util;

import com.lbiuse.wx.bean.Status;
import com.lbiuse.wx.bean.media.Media;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Http 工具
 */
public class WxHttpUtils {

    private final static Logger logger = Logger.getLogger(WxHttpUtils.class);

    private final static String CHARSET = "UTF-8";
    private static SSLSocketFactory sslSocketFactory = null;
    private static int timeout = 30000;

    static {
        TrustManager[] trustManagers = new TrustManager[]{new MyX509TrustManager()};
        KeyManager[] keyManagers = null;
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(keyManagers, trustManagers, new SecureRandom());
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (Exception e) {
            logger.error("创建 SSLSocketFactory 失败！");
            logger.error(e);
        }
    }

    public final static <T extends Status> T get(String url,
                                                 Map<String, String> params, Class<T> t) {
        StringBuilder sb = new StringBuilder(url);
        if (url.indexOf("?") == -1) {
            sb.append("?");
        } else {
            sb.append("&");
        }
        sb.append(MapUtil.join(params));
        url = sb.toString();
        return get(url, t);
    }

    public final static <T extends Status> T get(String url, Class<T> t) {
        try {
            String json = get(url);
            T result = JSONUtil.parse(json, t);
            return result;
        } catch (Exception e) {
            logger.error("GET 请求失败");
            logger.error("URL:" + url);
            logger.error(e);
            T result = null;
            try {
                result = t.newInstance();
            } catch (Exception e1) {
            }
            result.setErrcode(-2);
            result.setErrmsg("网络错误！");
            return result;
        }
    }

    public final static <T extends Status> T getForXml(String url, Class<T> t) {
        try {
            //TODO
            String json = get(url);
            T result = JSONUtil.parse(json, t);
            return result;
        } catch (Exception e) {
            logger.error("GET 请求失败");
            logger.error("URL:" + url);
            logger.error(e);
            T result = null;
            try {
                result = t.newInstance();
            } catch (Exception e1) {
            }
            result.setErrcode(-2);
            result.setErrmsg("网络错误！");
            return result;
        }
    }


    public final static <T extends Status> T post(String url,
                                                  Map<String, String> params, Class<T> t) {
        String data = MapUtil.join(params);
        return post(url, data, t);
    }

    public final static <T extends Status> T postForXml(String url,
                                                        String data, Class<T> t) {
        try {
            String xml = post(url, data);
            //TODO xml转bean
            xml = xml.replaceAll("<![CDATA[|]]>", "");

            T result = (T) XmlUtils.xmlStrToBean(xml, t);
            return result;
        } catch (Exception e) {
            logger.error("POST 请求失败");
            logger.error("URL:" + url);
            logger.error("DATA:" + data);
            logger.error(e);
            T result = null;
            try {
                result = t.newInstance();
            } catch (Exception e1) {
            }
            result.setErrcode(-2);
            result.setErrmsg("网络错误！");
            return result;
        }
    }


    public final static <T extends Status> T post(String url, String data,
                                                  Class<T> t) {
        try {
            String json = post(url, data);
            T result = JSONUtil.parse(json, t);
            return result;
        } catch (Exception e) {
            logger.error("POST 请求失败");
            logger.error("URL:" + url);
            logger.error("DATA:" + data);
            logger.error(e);
            T result = null;
            try {
                result = t.newInstance();
            } catch (Exception e1) {
            }
            result.setErrcode(-2);
            result.setErrmsg("网络错误！");
            return result;
        }
    }

    public final static <T extends Status> T uploadMedia(String url, File file,
                                                         Class<T> t) {
        try {
            String json = upload(url, file);
            T result = JSONUtil.parse(json, t);
            return result;
        } catch (Exception e) {
            logger.error("Upload 请求失败");
            logger.error("URL:" + url);
            logger.error("File:" + file.getAbsolutePath());
            logger.error(e);
            T result = null;
            try {
                result = t.newInstance();
            } catch (Exception e1) {
            }
            result.setErrcode(-2);
            result.setErrmsg("网络错误！");
            return result;
        }
    }

    // ----------------------------------------------------------------------------------------
    public static String get(String url) throws IOException {
        logger.info("GET:" + url);
        StringBuffer bufferRes = null;
        URL urlGet = new URL(url);
        HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
        if (http instanceof HttpsURLConnection) {
            ((HttpsURLConnection) http).setSSLSocketFactory(sslSocketFactory);
        }
        http.setConnectTimeout(timeout);
        http.setReadTimeout(timeout);
        http.setRequestMethod("GET");
        http.setRequestProperty("Content-Type",
                "application/x-www-form-urlencoded");
        http.setDoOutput(true);
        http.setDoInput(true);
        http.connect();
        InputStream in = http.getInputStream();
        BufferedReader read = new BufferedReader(new InputStreamReader(in,
                CHARSET));
        String valueString = null;
        bufferRes = new StringBuffer();
        while ((valueString = read.readLine()) != null) {
            bufferRes.append(valueString);
        }
        in.close();
        if (http != null) {
            http.disconnect();
        }
        logger.info(bufferRes.toString());
        return bufferRes.toString();
    }

    /**
     * 发送Get请求
     *
     * @param url
     * @param params
     * @return
     * @throws IOException
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static String get(String url, Map<String, String> params)
            throws KeyManagementException, NoSuchAlgorithmException,
            NoSuchProviderException, IOException {
        return get(initParams(url, params));
    }

    public static String post(String url) throws IOException {
        return post(url, null);
    }

    public static String post(String url, String data) throws IOException {
        logger.info("POST:" + url);
        StringBuffer bufferRes = null;
        URL urlGet = new URL(url);
        HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
        if (http instanceof HttpsURLConnection) {
            ((HttpsURLConnection) http).setSSLSocketFactory(sslSocketFactory);
        }
        http.setConnectTimeout(timeout);
        http.setReadTimeout(timeout);
        http.setRequestMethod("POST");
        http.setRequestProperty("Content-Type",
                "application/x-www-form-urlencoded");
        http.setDoOutput(true);
        http.setDoInput(true);
        http.connect();
        if (data != null && data.length() > 0) {
            OutputStream out = http.getOutputStream();
            out.write(data.getBytes(CHARSET));
            out.flush();
            out.close();
        }
        InputStream in = http.getInputStream();
        BufferedReader read = new BufferedReader(new InputStreamReader(in,
                CHARSET));
        String valueString = null;
        bufferRes = new StringBuffer();
        while ((valueString = read.readLine()) != null) {
            bufferRes.append(valueString);
        }
        in.close();
        if (http != null) {
            http.disconnect();
        }
        logger.info(bufferRes.toString());
        return bufferRes.toString();
    }

    /**
     * 上传媒体文件
     *
     * @param url
     * @param params
     * @param file
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws KeyManagementException
     */
    public static String upload(String url, File file) throws IOException,
            NoSuchAlgorithmException, NoSuchProviderException,
            KeyManagementException {
        String BOUNDARY = "----WebKitFormBoundaryiDGnV9zdZA1eM1yL";
        StringBuffer bufferRes = null;
        URL urlGet = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) urlGet.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty(
                "user-agent",
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36");
        conn.setRequestProperty("Charsert", "UTF-8");
        conn.setRequestProperty("Content-Type",
                "multipart/form-data; boundary=" + BOUNDARY);

        OutputStream out = new DataOutputStream(conn.getOutputStream());
        byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 定义最后数据分隔线
        StringBuilder sb = new StringBuilder();
        sb.append("--");
        sb.append(BOUNDARY);
        sb.append("\r\n");
        sb.append("Content-Disposition: form-data;name=\"media\";filename=\""
                + file.getName() + "\"\r\n");
        sb.append("Content-Type:application/octet-stream\r\n\r\n");
        byte[] data = sb.toString().getBytes();
        out.write(data);
        DataInputStream fs = new DataInputStream(new FileInputStream(file));
        int bytes = 0;
        byte[] bufferOut = new byte[1024];
        while ((bytes = fs.read(bufferOut)) != -1) {
            out.write(bufferOut, 0, bytes);
        }
        out.write("\r\n".getBytes());
        fs.close();
        out.write(end_data);
        out.flush();
        out.close();

        InputStream in = conn.getInputStream();
        BufferedReader read = new BufferedReader(new InputStreamReader(in,
                CHARSET));
        String valueString = null;
        bufferRes = new StringBuffer();
        while ((valueString = read.readLine()) != null) {
            bufferRes.append(valueString);
        }
        in.close();
        if (conn != null) {
            conn.disconnect();
        }
        return bufferRes.toString();
    }

    /**
     * 下载资源
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static Media downloadMedia(String url) throws IOException {
        logger.info("Download:" + url);
        Media media = null;
        URL urlGet = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) urlGet.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("GET");
        conn.connect();
        if (conn.getContentType().equalsIgnoreCase("text/plain")) {
            InputStream in = conn.getInputStream();
            String json = IOUtils.toString(in);
            in.close();
            media = JSONUtil.parse(json, Media.class);
        } else {
            BufferedInputStream bis = new BufferedInputStream(
                    conn.getInputStream());
            String ds = conn.getHeaderField("Content-disposition");
            String fullName = ds.substring(ds.indexOf("filename=\"") + 10,
                    ds.length() - 1);
            String relName = fullName.substring(0, fullName.lastIndexOf("."));
            String suffix = fullName.substring(relName.length() + 1);
            media = new Media(0, "");
            media.setFullName(fullName);
            media.setFileName(relName);
            media.setSuffix(suffix);
            media.setContentLength(conn.getHeaderField("Content-Length"));
            media.setContentType(conn.getHeaderField("Content-Type"));
            media.setFileStream(bis);
        }
        if (media == null) {
            media = new Media(-2, "网络错误！");
        }
        return media;
    }

    /**
     * 构造url
     *
     * @param url
     * @param params
     * @return
     */
    private static String initParams(String url, Map<String, String> params) {
        if (null == params || params.isEmpty()) {
            return url;
        }
        StringBuilder sb = new StringBuilder(url);
        if (url.indexOf("?") == -1) {
            sb.append("?");
        } else {
            sb.append("&");
        }
        boolean first = true;
        for (Entry<String, String> entry : params.entrySet()) {
            if (first) {
                first = false;
            } else {
                sb.append("&");
            }
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append("=");
            if (StringUtils.isNotEmpty(value)) {
                try {
                    sb.append(URLEncoder.encode(value, CHARSET));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    logger.error(url, e);
                }
            }
        }
        return sb.toString();
    }

}

/**
 * 证书管理
 */
class MyX509TrustManager implements X509TrustManager {

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }
}
